package com.lb.uts.client.util;

import com.lb.uts.client.exception.UTSException;
import org.springframework.aop.SpringProxy;
import org.springframework.aop.framework.AdvisedSupport;
import org.springframework.aop.framework.AopProxy;
import org.springframework.aop.support.AopUtils;
import org.springframework.cglib.proxy.Proxy;
import org.springframework.util.ClassUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * 用于获取被代理的类的方法的注解
 * 主要用于安全，如果没有{@link com.lb.uts.client.annotation.UtsTask}的方法不可以调用
 * @since 1.0.0
 */
public class AopTargetUtils {
    /**
     * 获取代理对象的目标对象
     *
     * @param proxy 代理对象
     * @return 目标对象
     */
    public static Object getTarget(Object proxy) {
        if (!AopUtils.isAopProxy(proxy)) {
            return proxy;//不是代理对象
        }

        Object target = null;

        try {
            if (AopUtils.isJdkDynamicProxy(proxy)) {
                target = getJdkDynamicProxyTargetObject(proxy);
            } else { //cglib
                target = getCglibProxyTargetObject(proxy);
            }
        } catch(Exception e) {
            throw new UTSException("gateway-0100", "不能取得代理对象的目标对象", e);
        }

        return target;
    }

    /**
     * 取得代理对象的目标对象的方法
     * @param proxy
     * @param methodName
     * @param parameterTypes
     * @return
     */
    public static Method getTargetMethod(Object proxy, String methodName, Class<?>... parameterTypes) {
        Object target = getTarget(proxy);
        try {
            return target.getClass().getMethod(methodName, parameterTypes);
        } catch (NoSuchMethodException e) {
            return null;
        }
    }

    /**
     * 取得代理对象方法的底层目标对象方法
     * @param proxyMethod
     * @return
     */
    public static Method getTargetMethod(Method proxyMethod) {
        Class<?> declaringClass = proxyMethod.getDeclaringClass();

        // 代理对象, SpringProxy 是所有 Spring 代理对象的超类
        if(SpringProxy.class.isAssignableFrom(declaringClass)) {
            Method targetMethod = null;

            // JDK 动态代理
            if(Proxy.isProxyClass(declaringClass)) {
                for(Class<?> face : declaringClass.getInterfaces()) {
                    try {
                        targetMethod = face.getMethod(proxyMethod.getName(), proxyMethod.getParameterTypes());
                        break;
                    } catch (NoSuchMethodException e) {
                    }
                }
            }

            // CGLIB 动态代理
            while(ClassUtils.isCglibProxyClass(declaringClass)) {
                declaringClass = declaringClass.getSuperclass();
                if(ClassUtils.isCglibProxyClass(declaringClass)) {
                    continue;
                }
                try {
                    targetMethod = declaringClass.getMethod(proxyMethod.getName(), proxyMethod.getParameterTypes());
                } catch(NoSuchMethodException e) {
                }
            }

            return targetMethod;
        } else {
            // 非代理对象
            return proxyMethod;
        }
    }


    private static Object getCglibProxyTargetObject(Object proxy) throws Exception {
        Field field = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0");
        field.setAccessible(true);
        Object dynamicAdvisedInterceptor = field.get(proxy);

        Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised");
        advised.setAccessible(true);

        Object target = ((AdvisedSupport) advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget();

        return target;
    }


    private static Object getJdkDynamicProxyTargetObject(Object proxy) throws Exception {
        Field field = proxy.getClass().getSuperclass().getDeclaredField("h");
        field.setAccessible(true);
        AopProxy aopProxy = (AopProxy) field.get(proxy);

        Field advised = aopProxy.getClass().getDeclaredField("advised");
        advised.setAccessible(true);

        Object target = ((AdvisedSupport) advised.get(aopProxy)).getTargetSource().getTarget();

        return target;
    }
}
